Skip to content

linux: fix HKDF TLS key derivation back to OpenSSL 3.0.8#1055

Merged
igaw merged 1 commit intolinux-nvme:masterfrom
cleech:hkdf-old-openssl
Aug 29, 2025
Merged

linux: fix HKDF TLS key derivation back to OpenSSL 3.0.8#1055
igaw merged 1 commit intolinux-nvme:masterfrom
cleech:hkdf-old-openssl

Conversation

@cleech
Copy link
Copy Markdown
Contributor

@cleech cleech commented Aug 26, 2025

Attempted fix for Issue #1053

I'm not crazy about rewriting things this way, but it passes the new tests when building with OpenSSL 3.0.8 and still works on my SPDK connection interoperability test.

OpenSSL prior to 3.3.1 had an issue with EVP_PKEY_CTX_add1_hkdf_info() where it acted like a 'set1' function instead of an 'add1' as documented. Work around that by building the entire info vector outside of the OpenSSL API and only calling this function once.

Comment thread src/nvme/linux.c Outdated
Comment thread src/nvme/linux.c Outdated
Comment thread src/nvme/linux.c Outdated
Comment thread src/nvme/linux.c Outdated
Comment thread src/nvme/linux.c Outdated
@igaw
Copy link
Copy Markdown
Collaborator

igaw commented Aug 27, 2025

I'm not crazy about rewriting things this way, but it passes the new tests when building with OpenSSL 3.0.8

Me neither. I think the current code is nicer to read. Maybe we could add a fallback implementation which is only selected for older version of OpenSSL. So when <= 3.3 get retired we can just rip out the the fallback implementation.

@cleech
Copy link
Copy Markdown
Contributor Author

cleech commented Aug 27, 2025

I'm not crazy about rewriting things this way, but it passes the new tests when building with OpenSSL 3.0.8

Me neither. I think the current code is nicer to read. Maybe we could add a fallback implementation which is only selected for older version of OpenSSL. So when <= 3.3 get retired we can just rip out the the fallback implementation.

Too much duplication, we'll get something compatible with 3.0.8 into a more palatable state.

@cleech
Copy link
Copy Markdown
Contributor Author

cleech commented Aug 27, 2025

Ok, how about a slightly different take. I think this is cleaner, and I ended up removing the hkdf_info_printf() function in favor of snprintf() calls because it just wasn't helping anymore.

I removed all the changes to the compat functions, because the entire point of them is to not change. But that does mean that the compat tests will fail in CI. Given that the old code is not only out-of-spec, but inconsistent across big/little endian systems and across openssl versions, I'm still not in favor of trying to maintain it.

@cleech cleech requested a review from hreinecke August 27, 2025 23:00
Copy link
Copy Markdown
Collaborator

@hreinecke hreinecke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This really looks better now.

Comment thread src/nvme/linux.c
Comment thread src/nvme/linux.c Outdated
*(uint16_t *)pos = htons(key_len & 0xFFFF);
pos += sizeof(uint16_t);

ret = snprintf(pos, 257, "%ctls13 HostNQN", 13);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'13' as argument? why? Please use 'strlen("tls13 HostNQN")' or something to make it clear that this is the length of the string.

Comment thread src/nvme/linux.c Outdated
*(uint16_t *)pos = htons(key_len & 0xFFFF);
pos += sizeof(uint16_t);

ret = snprintf(pos, 257, "%ctls13 nvme-tls-psk", 18);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, use 'strlen("tls13 nvme-tls-psk")' instead of hard-coded '18'.

@igaw
Copy link
Copy Markdown
Collaborator

igaw commented Aug 28, 2025

I removed all the changes to the compat functions, because the entire point of them is to not change. But that does mean that the compat tests will fail in CI. Given that the old code is not only out-of-spec, but inconsistent across big/little endian systems and across openssl versions, I'm still not in favor of trying to maintain it.

Well, do we want to break existing users? And I am sure there are users out there. Would it possible to issue a warning that a identifier has the wrong format?

@hreinecke
Copy link
Copy Markdown
Collaborator

I removed all the changes to the compat functions, because the entire point of them is to not change. But that does mean that the compat tests will fail in CI. Given that the old code is not only out-of-spec, but inconsistent across big/little endian systems and across openssl versions, I'm still not in favor of trying to maintain it.

Well, do we want to break existing users? And I am sure there are users out there. Would it possible to issue a warning that a identifier has the wrong format?

I would agree with @cleech , we should keep the original implementation as-is and not try to modify it. (Kinda the point of a compat implementation). And we do know that it generates errors, so that's actually not unexpected. And if the compat implementation generates different results for different openssl implementations then we need to store the various test vectors, not modifying the algorithm.

OpenSSL prior to 3.3.1 had an issue with EVP_PKEY_CTX_add1_hkdf_info()
where it acted like a 'set1' function instead of an 'add1' as
documented.  Work around that by building the entire info vector outside
of the OpenSSL API and only calling this function once.

This removes the hkdf_info_printf helper, which ended up being more of a
hinderence with this change, in favor of building the info vector with
calls like snprintf(p, 257, "%c%s", strlen(s), s) where HkdfLabel.label
and HkdfLabel.context have a maxiumum length of 256 each.

Signed-off-by: Chris Leech <[email protected]>
Copy link
Copy Markdown
Collaborator

@hreinecke hreinecke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks good now.

Comment thread src/nvme/linux.c
if (hkdf_info_printf(ctx, "tls13 HostNQN") <= 0) {
pos += ret;

ret = snprintf(pos, HKDF_INFO_CONTEXT_MAX + 1, "%c%s",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The size argument for the second snprintf needs to take into account that the previous snprintf already used up a bit of the buffer, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are limits on each field and the entire buffer is allocated to allow for the maximum of both. So I think it's Ok.

Copy link
Copy Markdown
Collaborator

@igaw igaw Aug 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, but I let you fix the static code analyzer reports :)

@igaw igaw merged commit eff0ffe into linux-nvme:master Aug 29, 2025
12 checks passed
@igaw
Copy link
Copy Markdown
Collaborator

igaw commented Aug 29, 2025

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants